﻿using System;
using System.ComponentModel;
using System.Xml.Linq;

namespace SharePointUtilitiesForDevelopers
{
    /// <summary>
    /// A domain class that represents SharePoint get list query options.
    /// </summary>
    [ReadOnly(true)]
    public class SharePointQueryOptions: INotifyPropertyChanged
    {
        #region Fields

        private bool _dateInUtc = true;
        private bool _includeMandatoryColumns = true;
        private string _folder = string.Empty;
        private string _pagingListItemCollectionPositionNext = string.Empty;
        private int _meetingInstanceID = -1;
        private bool _viewAttributesScopeRecursive;

        #endregion Fields

        #region Properties

        /// <summary>
        /// Gets or sets a value indicating whether dates are returned in Coordinated Universal Time (UTC) format.
        /// </summary>
        /// <value><c>true</c> if [date in UTC]; otherwise, <c>false</c>.</value>
        public bool DateInUtc
        {
            get { return _dateInUtc; }
            set
            {
                if (_dateInUtc != value)
                {
                    _dateInUtc = value;
                    OnPropertyChanged("DateInUtc");
                }
            }
        }
        
        /// <summary>
        /// Gets or sets the URL used to filter document library items for items in the specified folder. 
        /// This element is optional, and its default value is an empty string.
        /// </summary>
        /// <value>The folder.</value>
        public string Folder
        {
            get { return _folder; }
            set
            {
                if (_folder != value)
                {
                    _folder = value;
                    OnPropertyChanged("Folder");
                }
            }
        }
        
        /// <summary>
        /// Gets or sets a string that contains data needed to support paging, including a ListItemCollectionPositionNext attribute. 
        /// If left empty, the list items returned start from the beginning of the list. 
        /// Client applications should supply the value of a server-supplied bookmark. 
        /// The server returns the next page of data based on the bookmark supplied. 
        /// This element is optional, and its default value is an empty string.
        /// </summary>
        /// <value>The paging.</value>
        public string PagingListItemCollectionPositionNext
        {
            get { return _pagingListItemCollectionPositionNext; }
            set
            {
                if (_pagingListItemCollectionPositionNext != value)
                {
                    _pagingListItemCollectionPositionNext = value;
                    OnPropertyChanged("PagingListItemCollectionPositionNext");
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether mandatory system columns are returned in addition to the fields specified by the 
        /// viewFields parameter (for example, owsHiddenVersion, dependent fields, and required fields). 
        /// This element is optional, and its default value is TRUE.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if include mandatory columns; otherwise, <c>false</c>.
        /// </value>
        public bool IncludeMandatoryColumns
        {
            get { return _includeMandatoryColumns; }
            set
            {
                if (_includeMandatoryColumns != value)
                {
                    _includeMandatoryColumns = value;
                    OnPropertyChanged("IncludeMandatoryColumns");
                }
            }
        }
        
        /// <summary>
        /// Gets or sets an integer value where a positive number represents a specific meeting instance. 
        /// Negative numbers have the following meanings: -3 = UnSpecified, -2 = AllWithSeries, -1 = AllButSeries, 0 = Series. 
        /// This element is optional, and its default value is -1. 
        /// Negative values correspond to values of the Microsoft.SharePoint.Meetings.SPMeeting.SpecialInstance enumeration.
        /// </summary>
        /// <value>The meeting instance ID.</value>
        public int MeetingInstanceID
        {
            get { return _meetingInstanceID; }
            set
            {
                if (_meetingInstanceID != value)
                {
                    _meetingInstanceID = value;
                    OnPropertyChanged("MeetingInstanceID");
                }
            }
        }
        
        /// <summary>
        /// Gets or sets a string representing all attributes returned as part of the View element when retrieving a view through 
        /// the GetView method. This element is optional, and its default value is empty. 
        /// If a viewName parameter is supplied, the view attributes from the persisted view are used. 
        /// When this argument is supplied, it overrides any view attributes that can be retrieved from the persisted 
        /// view specified by the viewName parameter. This element is optional and its default value is an empty string. 
        /// To return all documents in a library, the ViewAttributes element is used as follows: &lt;ViewAttributes Scope="Recursive" /&gt;.
        /// </summary>
        /// <value>The view attributes.</value>
        public bool ViewAttributesScopeRecursive
        {
            get { return _viewAttributesScopeRecursive; }
            set 
            {
                if (_viewAttributesScopeRecursive != value)
                {
                    _viewAttributesScopeRecursive = value;
                    OnPropertyChanged("ViewAttributesScopeRecursive");
                }
            }
        }

        #endregion Properties

        #region Events

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Called when a property changes.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        protected virtual void OnPropertyChanged(string propertyName)
        {
            if(PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion Events
    }

    /// <summary>
    /// Contains extension methods for the <see cref="SharePointQueryOptions"/> class.
    /// </summary>
    public static class SharePointQueryOptionsExtensionMethods
    {

        /// <summary>
        /// Gets query options XML definition from the xml string.
        /// </summary>
        /// <param name="queryOptionsDefinition">The query options definition.</param>
        /// <returns>An <see cref="XElement"/> that represents the query options.</returns>
        public static XElement ToQueryOptionsXElement(this string queryOptionsDefinition)
        {
            XElement queryOptions = null;

            if(!string.IsNullOrEmpty(queryOptionsDefinition))
            {
                queryOptions = XElement.Parse(queryOptionsDefinition);
            }

            return queryOptions.ToSharePointQueryOptions().ToQueryOptionsXElement();
        }


        /// <summary>
        /// Returns the query options XML definition based on the values in the options.
        /// </summary>
        /// <param name="options">The options.</param>
        /// <returns>An <see cref="XElement"/> that represents the query options.</returns>
        public static XElement ToQueryOptionsXElement(this SharePointQueryOptions options)
        {
            XNamespace ns = string.Empty;
            var queryOptions = new XElement(ns + "QueryOptions");

            if(options != null)
            {
                queryOptions.Add(new XElement(ns + "IncludeMandatoryColumns", options.IncludeMandatoryColumns.ToString().ToUpperInvariant()));
                queryOptions.Add(new XElement(ns + "DateInUtc", options.DateInUtc.ToString().ToUpperInvariant()));
                queryOptions.Add(new XElement(ns + "MeetingInstanceID", options.MeetingInstanceID.ToString()));
                queryOptions.Add(new XElement(ns + "Folder", options.Folder));
                queryOptions.Add(new XElement(ns + "Paging", new XAttribute("ListItemCollectionPositionNext", options.PagingListItemCollectionPositionNext)));
                queryOptions.Add(new XElement(ns + "ViewAttributes", options.ViewAttributesScopeRecursive ? new XAttribute("Scope", "Recursive") : null));
            }

            return queryOptions;

        }

        /// <summary>
        /// Returns the query options as XML.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <returns>A <see cref="SharePointQueryOptions"/> object based on the XML.</returns>
        public static SharePointQueryOptions ToSharePointQueryOptions(this XElement element)
        {
            if(element == null)
            {
                return new SharePointQueryOptions();
            }

            return new SharePointQueryOptions
                              {
                                  DateInUtc = SharePointXml.GetChildElementValueAsBoolean(element, "DateInUtc"),
                                  IncludeMandatoryColumns =
                                      SharePointXml.GetChildElementValueAsBoolean(element, "IncludeMandatoryColumns"),
                                  MeetingInstanceID = (SharePointXml.GetChildElementValueAsNullableInteger(element, "MeetingInstanceID") ?? -1),
                                  Folder = SharePointXml.GetChildElementValueAsString(element, "Folder"),
                                  PagingListItemCollectionPositionNext = SharePointXml.GetChildElementAttributeValueAsString(element, "Paging", "ListItemCollectionPositionNext"),
                                  ViewAttributesScopeRecursive = (string.Compare(SharePointXml.GetChildElementAttributeValueAsString(element, "ViewAttributes", "Scope"), "Recursive", StringComparison.InvariantCultureIgnoreCase) == 0) 
                              };
           
        }

    }
}
